/**
 *	@file	safari_data.c
 *	@brief	サファリ関連データ
 *	@author	Miyuki Iwasawa
 *	@date	08.10.16
 */

#include "common.h"
#include "savedata/savedata.h"
#include "savedata/safari_sv.h"
#include "field/fieldsys.h"
#include "field/evwkdef.h"
#include "field/eventflag.h"
#include "field/sysflag.h"
#include "fielddata/script/saveflag.h"
#include "safari/safari_sys.h"
#include "system/timezone.h"
#include "tel/pmtel_reserve_def.h"

typedef struct _SAFARI_ENC_HEAD{
	u8 ex_prm_num[SAFARI_ENC_MODE_MAX];	//拡張パラメータ数
	u8 dmy[3];
}SAFARI_ENC_HEAD;

typedef union SAFARI_ENC_EXPRM{
	struct{
		u8 attr1;
		u8 lv1;
		u8 attr2;
		u8 lv2;
	};
	u8	prm[4];
}SAFARI_ENC_EXPRM;

typedef struct _SAFARI_ENC_OFS{
	SAFARI_ENC_POKE* def_poke;
	SAFARI_ENC_POKE* ex_poke;
	SAFARI_ENC_EXPRM* ex_prm;
	int timeofs_siz;
	u8	ex_num;
}SAFARI_ENC_OFS;

////////////////////////////////////////////////////////////////////
//サファリオブジェト用データ
////////////////////////////////////////////////////////////////////
static const SAFARI_OBJ_PARAM SafariObjParamTable[SAFARI_OBJ_ID_MAX] =  {
{	BMID_FIELD_SAF_GO01	, 0,	1,	1,	0,	SAFARI_ATTR_GRASS	},	//	植え込み
{	BMID_FIELD_SAF_GO02	, 0,	1,	1,	0,	SAFARI_ATTR_GRASS	},	//	赤い花
{	BMID_FIELD_SAF_GO03	, 0,	1,	1,	0,	SAFARI_ATTR_GRASS	},	//	白い花
{	BMID_FIELD_SAF_TO01	, 0,	2,	2,	0,	SAFARI_ATTR_TREE	},	//	木
{	BMID_FIELD_SAF_TO02	, 0,	2,	2,	0,	SAFARI_ATTR_TREE	},	//	切り株
{	BMID_FIELD_SAF_TO03	, 0,	2,	2,	0,	SAFARI_ATTR_TREE	},	//	枝
{	BMID_FIELD_SAF_RO01	, 0,	1,	1,	0,	SAFARI_ATTR_ROCK	},	//	小さな岩
{	BMID_FIELD_SAF_RO02	, 0,	2,	2,	0,	SAFARI_ATTR_ROCK	},	//	大きな岩
{	BMID_FIELD_SAF_RO03	, 0,	2,	2,	0,	SAFARI_ATTR_ROCK	},	//	苔の岩
{	BMID_FIELD_SAF_WO01 , 0,	2,	2,	0,	SAFARI_ATTR_WATER	},	//	水溜り
{	BMID_FIELD_SAF_WO02 , 1,	2,	2,	0,	SAFARI_ATTR_WATER	},	//	噴水
{	BMID_FIELD_SAF_WO03 , 0,	2,	2,	0,	SAFARI_ATTR_WATER	},	//	水飲み場
{	BMID_FIELD_SAF_EO01	, 0,	2,	1,	0,	SAFARI_ATTR_NONE	},	//	ベンチ
{	BMID_FIELD_SAF_EO02	, 0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	柵１
{	BMID_FIELD_SAF_EO03	, 0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	柵２
{	BMID_FIELD_SAF_EO04	, 0,	2,	1,	0,	SAFARI_ATTR_NONE	},	//	柵３
{	BMID_FIELD_SAF_EO05	, 0,	1,	2,	0,	SAFARI_ATTR_NONE	},	//	柵４
{	BMID_FIELD_SAF_EO06	, 0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	看板
{	BMID_FIELD_SAF_EO07M, 0,	1,	1,	1,	SAFARI_ATTR_NONE	},	//	自分の石像
{	BMID_FIELD_SAF_EO08M, 0,	1,	1,	1,	SAFARI_ATTR_NONE	},	//　自分の旗
{	BMID_FIELD_SAF_EO09,  0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	街灯
{	BMID_FIELD_SAF_EO10,  0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	道しるべ右
{	BMID_FIELD_SAF_EO11,  0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	道しるべ左
{	BMID_FIELD_SAF_EO12,  0,	1,	1,	0,	SAFARI_ATTR_NONE	},	//	ゴミ箱
};

/*
 *	@brief	サファリオブジェクトのパラメータを構造体型にコピー
 *
 *	＊領域は呼び出し側で確保すること
 */
void SafariData_GetObjectParam(SAFARI_OBJ_PARAM* prm,SAFARI_OBJ_ID id,u8 my_sex)
{
	if(id >= SAFARI_OBJ_ID_MAX){
		GF_ASSERT_MSG(0,"サファリオブジェのID %dが不正です\n",id);
		id = 0;
	}
	*prm = SafariObjParamTable[id];

	if(prm->sex_f && (my_sex == PM_FEMALE)){
		prm->obj_id += 1;
	}
}

#define ENC_TBL_MAX	(5)

/*
 *	@brief	おんなじエンカウントデータかどうか？
 */
static BOOL sub_IsSameEncData(ENC_COMMON_DATA* enc_b,ENC_COMMON_DATA* enc_a)
{
	if(	enc_b->MonsNo != enc_a->MonsNo ||
		enc_b->LvMax != enc_a->LvMax ||
		enc_b->LvMin != enc_a->LvMin){
		return FALSE;
	}
	return TRUE;
}
/*
 *	@brief	新旧エンカウントテーブル比較
 */
static BOOL sub_IsSameEncTable(ENC_COMMON_DATA* enc_b,ENC_COMMON_DATA* enc_a)
{
	int i;
	for(i = 0;i < SAFARI_ENC_POKE_NUM;i++){
		if(!sub_IsSameEncData(&enc_b[i],&enc_a[i])){
			return FALSE;
		}
	}
	return TRUE;
}

/*
 *	@brief	エンカウント率更新履歴テーブルの重複登録チェック
 */
static BOOL sub_IsSameRecordExist(u8 map_id,u8* tbl,u8 num)
{
	int i;

	for(i = 0;i < num;i++){
		if(map_id == tbl[i]){
			return TRUE;
		}
	}
	return FALSE;
}

/**
 *	@brief	サファリのレベルアップ処理(日付変更処理で一日一回だけ呼び出す)
 */
void SafariData_Update(SAVEDATA* save,s32 diff_days)
{
	u8 id;
	int i,j,num;

	u8	history[SAFARIL_BLOCK_MAX];
	ENC_COMMON_DATA* enc_b[SAFARIL_BLOCK_MAX][ENC_TBL_MAX];
	ENC_COMMON_DATA* enc_a;

	PMTEL_SAVE* tel = SaveData_GetPMTelSave(save);
	SAFARI_SAVE* safari = SaveData_GetSafariSave(save);
	SAFARI_LAYOUT* layout = SafariSv_GetLayoutPointer(safari,SAFARI_LAYOUT_MINE);	//レベルアップは自分のだけ

	//水パラメータを持つか持たないかのフラグデータ
	static const u16 water_param_exist = 0xFFFF;	//1111 1111 1111 1111

	if(SafariSv_GetObjLevel(safari) == 0 || diff_days <= 0){
		return;	//カスタマイズがまだ非有効 or 差分がマイナス
	}
	//サファリゲーム中だったら遅延
	if(SysFlag_SafariCheck(SaveData_GetEventWork(save))){
		int delay_ct = SafariSv_GetLayoutLevelUpDelayCount(safari);
		SafariSv_SetLayoutLevelUpDelayCount(safari,delay_ct+diff_days);
		return;
	}
	num = 0;
	MI_CpuClear8(history,sizeof(u8)*SAFARIL_BLOCK_MAX);
	MI_CpuClear8(enc_b,4*SAFARIL_BLOCK_MAX*ENC_TBL_MAX);

	//レベルアップ前のエンカウントテーブル取得
	for(i = 0 ;i < SAFARIL_BLOCK_MAX;i++){
		for(j = 0;j < 1+((water_param_exist>>i)&1)*4;j++){
			enc_b[i][j] = FieldSafari_AllocEncountTbl(layout,i,j,TIMEZONE_GS_MORNING,HEAPID_WORLD);
		}
	}
	//レベルアップ
	SafariSv_LayoutLevelUp(safari,diff_days);

	//レベルアップ後のエンカウントテーブルと比較
	for(i = 0;i < SAFARIL_BLOCK_MAX;i++){
		id = layout->block[i].map_id;

		//既に更新履歴があるmap_idは調べなくていい
		if(sub_IsSameRecordExist(id,history,num)){
			continue;
		}
		for(j = 0;j < 1+((water_param_exist>>i)&1)*4;j++){
			enc_a = FieldSafari_AllocEncountTbl(layout,i,j,TIMEZONE_GS_MORNING,HEAPID_WORLD);

			if(sub_IsSameEncTable(enc_b[i][j],enc_a)){
				sys_FreeMemoryEz(enc_a);	//同じ
				continue;
			}
			//違うので履歴保存
			if(!sub_IsSameRecordExist(id,history,num)){
				history[num++] = id;
			}
			sys_FreeMemoryEz(enc_a);
		}
	}

	//メモリ解放
	for(i = 0 ;i < SAFARIL_BLOCK_MAX;i++){
		for(j = 0;j < 1+((water_param_exist>>i)&1)*4;j++){
			if(enc_b[i][j] != NULL){
				sys_FreeMemoryEz(enc_b[i][j]);
			}
		}
	}
	//電話セーブデータにエンカウント率更新履歴を保存
	if(num == 0){
		//既存の着信予約をリセット
		PMTelSv_CallReserveReset(tel,PMTEL_RESERVE_SAFARI_MAP_UPDATE);
		PMTelSv_SafariUpdateHistorySet(tel,NULL,0);
	}else{
		PMTelSv_CallReserveSet(tel,PMTEL_RESERVE_SAFARI_MAP_UPDATE);
		PMTelSv_SafariUpdateHistorySet(tel,history,num);
	}
}

/*
 *	@brief	サファリのエンカウントテーブルをアロケーションして取得する
 *
 *	＊呼び出し側が利用後に解放すること！
 */
ENC_COMMON_DATA* FieldSafari_AllocEncountTbl(
	SAFARI_LAYOUT* layout,SAFARI_BLOCK_ID block_id,SAFARI_ENC_MODE mode,int time_zone,int heapID)
{
	int i,ofs;
	u8* buf;
	ENC_COMMON_DATA* data;
	SAFARI_BLOCK* pb;
	SAFARI_ENC_HEAD* head;
	SAFARI_ENC_OFS* ofs_tbl;

	pb = &layout->block[block_id];

	data = sys_AllocMemoryLo(heapID,sizeof(ENC_COMMON_DATA)*SAFARI_ENC_POKE_NUM);

	for(i = 0;i < SAFARI_ENC_POKE_NUM;i++){	//保険の初期化
		if(mode == SAFARI_ENC_GROUND){
			data[i].MonsNo = MONSNO_KORATTA;
		}else{
			data[i].MonsNo = MONSNO_KOIKINGU;
		}
		data[i].LvMax = data[i].LvMin = 5;
	}

	//ファイル読み込み
	buf = (u8*)ArcUtil_Load( ARC_SAFARI_ENC,pb->map_id,FALSE,heapID, ALLOC_BOTTOM);
	if(buf == NULL){
		return data;
	}

	//ヘッダを取得
	head = (SAFARI_ENC_HEAD*)buf;
	ofs_tbl = sys_AllocMemoryLo(heapID,sizeof(SAFARI_ENC_OFS)*SAFARI_ENC_MODE_MAX);
	ofs = sizeof(SAFARI_ENC_HEAD);
	for(i = 0;i < SAFARI_ENC_MODE_MAX;i++){
		ofs_tbl[i].def_poke = (SAFARI_ENC_POKE*)(&buf[ofs]);
		ofs += sizeof(SAFARI_ENC_POKE)*SAFARI_ENC_POKE_NUM*3;
		ofs_tbl[i].ex_poke = (SAFARI_ENC_POKE*)(&buf[ofs]);

		ofs_tbl[i].ex_num = head->ex_prm_num[i];
		ofs_tbl[i].timeofs_siz = sizeof(SAFARI_ENC_POKE)*ofs_tbl[i].ex_num;
		ofs += ofs_tbl[i].timeofs_siz*3;
		ofs_tbl[i].ex_prm = (SAFARI_ENC_EXPRM*)(&buf[ofs]);
		ofs += sizeof(SAFARI_ENC_EXPRM)*ofs_tbl[i].ex_num;
	}

	//デフォルトテーブルを取得
	if(ofs_tbl[SAFARI_ENC_WATER].ex_num == 0 && mode > SAFARI_ENC_GROUND){	//水データがない
		sys_FreeMemoryEz( ofs_tbl );
		sys_FreeMemoryEz( (void*)buf );
		return data;
	}
	ofs = SAFARI_ENC_POKE_NUM*time_zone;
	for(i = 0;i < SAFARI_ENC_POKE_NUM;i++){
		SAFARI_ENC_POKE* sp =
			(SAFARI_ENC_POKE*)&(ofs_tbl[mode].def_poke[ofs+i]);
		data[i].MonsNo = sp->monsno;
		data[i].LvMax = data[i].LvMin = sp->level;
	}
	//ブロックレベルによる上書き
	{
		SAFARI_BLOCK_LEVEL lv_tbl;
		u8 up_num = 0;

		//ブロックレベルテーブル取得
		SafariData_GetBlockAttrLevel(layout,block_id,&lv_tbl);

		for(i = 0;i < ofs_tbl[mode].ex_num;i++){
			int idx;
			SAFARI_ENC_EXPRM* pex = &ofs_tbl[mode].ex_prm[i];

			if(lv_tbl.level[pex->attr1-1] < pex->lv1){
				continue;
			}
			if(pex->attr2 && (lv_tbl.level[pex->attr2-1] < pex->lv2)){
				continue;
			}
			idx = ofs_tbl[mode].ex_num*time_zone+i;
			data[up_num].MonsNo = ofs_tbl[mode].ex_poke[idx].monsno;
			data[up_num].LvMax = data[up_num].LvMin = ofs_tbl[mode].ex_poke[idx].level;
			if(++up_num > (SAFARI_ENC_POKE_NUM-1)){
				break;	//maxチェック
			}
		}
	}
	sys_FreeMemoryEz( ofs_tbl );
	sys_FreeMemoryEz( (void*)buf );
	return data;
}

static const u8 enc_attr_prm_tbl[][5] = {
 {1,5,10,15,20},
 {2,6,11,16,21},
 {3,7,12,17,22},
 {4,8,13,18,23},
};

static u8 enc_AttrParamGet(u8 map_level,SAFARI_ATTR attr)
{
	int i;

	if(attr == SAFARI_ATTR_NONE){
		return 0;
	}
	for(i = 0;i < 5;i++){
		if(map_level < enc_attr_prm_tbl[attr-1][i]){
			return i+1;
		}
	}
	if(map_level < 25){
		return 6;
	}
	return 7;
}

/**
 *	@brief	サファリブロックレベル取得
 */
void SafariData_GetBlockAttrLevel(SAFARI_LAYOUT* layout,int block_id,SAFARI_BLOCK_LEVEL* pl)
{
	int i,tmp;
	u8 map_level;
	SAFARI_BLOCK* pb;
	SAFARI_OBJ*	obj;
	SAFARI_OBJ_PARAM	prm;

	pb = &layout->block[block_id];
	map_level = (layout->map_level[pb->map_id])/10;

	MI_CpuClear8(pl,sizeof(SAFARI_BLOCK_LEVEL));
	for( i = 0;i < pb->obj_ct; i++){
		obj = &pb->obj[i];
		SafariData_GetObjectParam(&prm,obj->id,PM_NEUTRAL);	//性別は気にしなくて良いので固定値にしている
		if(prm.attr == SAFARI_ATTR_NONE){
			continue;
		}
		pl->obj_num[prm.attr-1]++;
		tmp = enc_AttrParamGet(map_level,prm.attr);
		if((tmp + pl->level[prm.attr-1]) > 255){
			pl->level[prm.attr-1] = 255;
		}else{
			pl->level[prm.attr-1] += tmp;
		}
	}
}
